package com.pai4j.pgc.service;
import com.pai4j.common.bean.PAIPageResponseBeanUtil;
import com.pai4j.common.constants.WebConstant;
import com.pai4j.common.enums.RedisKeyEnum;
import com.pai4j.common.enums.RoleCodeEnum;
import com.pai4j.common.util.JsonUtil;
import com.pai4j.common.util.RedisUtil;
import com.pai4j.domain.vo.request.subjectaccount.CreateSubjectRequest;
import com.pai4j.domain.vo.request.subjectaccount.UpdateSubjectRequest;
import com.pai4j.domain.vo.response.UserBaseResponseInfoVO;
import com.pai4j.domain.vo.response.subjectaccount.ArticleBaseResponseVO;
import com.pai4j.domain.vo.response.subjectaccount.SubjectBaseResponseVO;
import com.pai4j.pgc.dao.IArticleDAO;
import com.pai4j.pgc.dao.ISubjectDAO;
import com.pai4j.pgc.entity.ArticleEntity;
import com.pai4j.pgc.entity.SubjectEntity;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;

/**
 * 专题服务
 *
 * @author: CYM-pai
 * @date: 2025/05/01 12:44
 **/

@Slf4j
@Service
public class SubjectService {
    
    @Resource
    private ISubjectDAO subjectDAO;
    @Resource
    private IArticleDAO articleDAO;
    @Resource
    private AccountService accountService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private SubjectAccountService subjectAccountService;
    
    
    /**
     * 创建专题
     *
     * @param subjectRequest
     * @return
     */
    public Boolean create(CreateSubjectRequest subjectRequest) {
        /**
         * 1. 持久化专题到数据库
         */
        SubjectEntity subjectEntity = this.convertEntity(subjectRequest);
        subjectEntity  = subjectDAO.save(subjectEntity);
        /**
         * 2. 缓存专题详情到redis
         */
        this.refreshSubjectDetailCache(subjectEntity);
        return true;
        
    }
    
    /**
     * 转换专题请求为专题实体
     *
     * @param subjectRequest
     * @return
     */
    
    private SubjectEntity convertEntity(CreateSubjectRequest subjectRequest) {
        // 获取专题，以及当前日期时间
        Long subjectId = subjectRequest.getId();
        String author = subjectRequest.getAuthor();
        Date now = new Date();
        SubjectEntity subjectEntity;
        
        // 如果专题ID不为空，则执行更新操作
        if (subjectId != null) {
            // 从数据库中查找专题实体
            subjectEntity = subjectDAO.findById(subjectId).orElse(null);
            // 断言专题存在，否则抛出异常
            Assert.notNull(subjectEntity, "专题更新失败: 专题不存在！");
            // 断言专题作者匹配，以验证更新权限
            Assert.isTrue(ObjectUtils.equals(subjectEntity.getAuthor(), author), "没有更新权限！");
        } else {
            // 如果专题ID为空，则执行创建新专题操作
            subjectEntity = new SubjectEntity();
            // 设置创建日期、创建者、作者和初始状态
            subjectEntity.setCreateDate(now);
            subjectEntity.setCreateBy(author);
            subjectEntity.setAuthor(author);
            subjectEntity.setName(subjectRequest.getName());
            subjectEntity.setRoleCode(RoleCodeEnum.SUBJECT_ACCOUNT.getCode());
            // 设置删除标识为未删除
            subjectEntity.setDeleteFlag(com.pai4j.common.enums.DeleteFlagCodeEnum.UN_DELETE.getCode());
        }
        
        // 从请求中复制数据到专题实体
        subjectEntity.setCover(subjectRequest.getCover());
        subjectEntity.setDescription(subjectRequest.getDescription());
        subjectEntity.setSigns(subjectRequest.getSigns());
        // 返回填充完毕的文章实体
        return subjectEntity;
    }
    /**
     *  从缓存中查询当前专题信息
     *
     * @author: CYM-pai
     * @date: 2025/05/02 11:56
     **/
    public SubjectBaseResponseVO findSubjectInfoFromCache(String subjectAccount) {
        
        if (subjectAccount == null) {
            return null;
        }
        //建立文章详情缓存查询key
        String cacheKey = RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(subjectAccount);
        String cache = redisUtil.get(cacheKey);
        SubjectBaseResponseVO subjectBaseResponseVO;
        subjectBaseResponseVO = JsonUtil.fromJson(cache, SubjectBaseResponseVO.class);
        return subjectBaseResponseVO;
    }
    
    /**
     *  批量查询专题信息
     *
     * @author: CYM-pai
     * @date: 2025/05/02 11:56
     **/
    public Map<String, SubjectBaseResponseVO> batchGet(Set<String> subjectAccounts) {
        if (CollectionUtils.isEmpty(subjectAccounts)) {
            return Collections.emptyMap();
        }
        Map<String, SubjectBaseResponseVO>  resultMap = this.batchGetSubjectInfo(subjectAccounts);
        return resultMap;
    }
    
    /**
     *  批量查询专题信息
     *
     * @author: CYM-pai
     * @date: 2025/05/02 11:56
     **/
    private Map<String, SubjectBaseResponseVO> batchGetSubjectInfo(Set<String> subjectAccounts) {
        
        return this.batchGetSubjectInfoFromCache(new ArrayList<>(subjectAccounts));
        
    }
    /**
     *  从缓存中批量查询专题信息
     *
     * @author: CYM-pai
     * @date: 2025/05/02 16:11
     **/
    private Map<String, SubjectBaseResponseVO> batchGetSubjectInfoFromCache(List<String> subjectAccountList) {
        // 如果文章ID列表为空，则直接返回空映射
        if (CollectionUtils.isEmpty(subjectAccountList)) {
            return Collections.emptyMap();
        }
        // 初始化批量查询结果的映射
        Map<String , SubjectBaseResponseVO> batchGetResult = new HashMap<>();
        // 构造批量查询redis的缓存key
        List<String> cacheKeys = subjectAccountList.stream().map(
                        id -> RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(String.valueOf(id)))
                .collect(Collectors.toList());
        // 批量查询redis
        List<String> caches = redisUtil.mget(cacheKeys);
        // 遍历查询结果，填充批量查询结果的映射
        for (int i = 0; i < subjectAccountList.size(); i++) {
            String subjectId = subjectAccountList.get(i);
            String cache = caches.get(i);
            SubjectBaseResponseVO subjectBaseResponseVO;
            // 如果缓存不为空，则解析缓存为文章详情对象
            if (StringUtils.isNotBlank(cache)) {
                
                subjectBaseResponseVO = JsonUtil.fromJson(cache, SubjectBaseResponseVO.class);
                String  author = subjectBaseResponseVO.getAuthor();
                UserBaseResponseInfoVO userBaseResponseInfoVO = accountService.accountInfo(author);
                subjectBaseResponseVO.setUserInfo(userBaseResponseInfoVO);
            } else {
                // 如果缓存为空，则刷新redis中的文章详情缓存，并获取文章详情对象
                SubjectEntity   subjectEntity = this.refreshSubjectRedisDetailCache(subjectId);
                String author = subjectEntity.getAuthor();
                UserBaseResponseInfoVO userBaseResponseInfoVO = accountService.accountInfo(author);
                subjectBaseResponseVO = subjectAccountService.convertBaseVo(subjectEntity);
                subjectBaseResponseVO.setUserInfo(userBaseResponseInfoVO);
            }
            // 将文章ID与文章详情对象映射存入结果中
            batchGetResult.put(subjectId, subjectBaseResponseVO);
        }
        // 返回批量查询结果的映射
        return batchGetResult;
    }
    
    /**
     * 刷新专题redis缓存
     *
     * @param subjectId
     * @return
     */
    private SubjectEntity refreshSubjectRedisDetailCache(String subjectId) {
        if (subjectId == null) {
            return null;
        }
        SubjectEntity subjectEntity = subjectDAO.findById(Long.valueOf(subjectId)).orElse(null);
        return this.refreshSubjectRedisDetailCache(subjectEntity);
    }
    
    /**
     * 刷新专题redis缓存
     *
     * @param subjectEntity
     * @return
     */
    private SubjectEntity refreshSubjectRedisDetailCache(SubjectEntity subjectEntity) {
        if (subjectEntity == null) {
            return null;
        }
        //建立专题详情缓存key
        String cacheKey = RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(String.valueOf(subjectEntity.getId()));
        // 每个专题有自己的失效时间，避免缓存雪崩
        redisUtil.set(cacheKey, JsonUtil.toJsonString(subjectEntity), RedisKeyEnum.SUBJECT_DETAIL_CACHE.getExpireTime());
        return subjectEntity;
    }
    
    /**
     * 刷新专题redis缓存,无返回值
     *
     * @param subjectEntity
     */
    private void refreshSubjectDetailCache(SubjectEntity subjectEntity) {
        if (subjectEntity == null) {
            return;
        }
        String author = subjectEntity.getAuthor();
        UserBaseResponseInfoVO userBaseResponseInfoVO = accountService.accountInfo(author);
        SubjectBaseResponseVO subjectBaseResponseVO = subjectAccountService.convertBaseVo(subjectEntity);
        subjectBaseResponseVO.setUserInfo(userBaseResponseInfoVO);
        String key = RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(String.valueOf(subjectBaseResponseVO.getId()));
        redisUtil.set(key, JsonUtil.toJsonString(subjectBaseResponseVO),  RedisKeyEnum.SUBJECT_DETAIL_CACHE.getExpireTime());
        subjectAccountService.refreshSubjectListCache(author);
    }
    
   
    /**
     *  更新专题信息
     *
     * @author: CYM-pai
     * @date: 2025/05/07 15:49
     **/
    public Boolean update(UpdateSubjectRequest subjectRequest) {
        //1、首先查询缓存判断专题是否存在，不存在直接查库
        Long subjectId = subjectRequest.getId();
        SubjectEntity subjectEntity = this.refreshAndGetSubjectEntityFromCache(String.valueOf(subjectId), true);
        if (subjectEntity == null) {
            // 缓存不存在主动查询数据库
             subjectEntity = subjectDAO.findById(subjectId).orElse(null);
            if (subjectEntity == null) {
                // 数据库专题不存在，这里缓存空值，防止redis击穿
                this.refreshSubjectNoneCache(subjectId);
            }
            
            return false;
        }
        subjectEntity.setName(subjectRequest.getName());
        subjectEntity.setDescription(subjectRequest.getDescription());
        subjectEntity.setCover(subjectRequest.getCover());
        subjectEntity.setSigns(subjectRequest.getSigns());
        subjectEntity.setUpdateBy(subjectEntity.getAuthor());
        subjectEntity.setUpdateDate(new Date());
        subjectDAO.save(subjectEntity);
        this.refreshSubjectDetailCache(subjectEntity);
        return true;
    }
    
    
    /**
     * 刷新并获取专题实体信息
     * 此方法首先尝试从缓存中获取专题信息如果信息已缓存且不需要刷新，则直接返回缓存信息
     * 否则，将从数据库中查询最新信息，并更新缓存
     *
     * @param subjectId 专题ID，用于查询专题信息
     * @param refreshCache 可变参数，指示是否需要刷新缓存如果传入，则表示需要刷新
     *
     */
    private SubjectEntity refreshAndGetSubjectEntityFromCache(String subjectId, boolean ... refreshCache) {
        // 构造Redis缓存的键
        String key = RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(subjectId);
        // 从Redis中获取缓存的专题信息
        String val = redisUtil.get(key);
        // 判断是否需要刷新缓存
        boolean refresh = refreshCache != null && refreshCache.length > 0 && refreshCache[0];
        // 如果不需要刷新缓存且缓存值存在，则直接返回缓存的用户信息
        if (!refresh && StringUtils.isNotBlank(val)) {
            return JsonUtil.fromJson(val, SubjectEntity.class);
        }
        // 从数据库中查询专题信息
        SubjectEntity subjectEntity = subjectDAO.findById(Long.valueOf(subjectId)).orElse(null);
        // 如果查询到专题信息，则更新缓存
        if (subjectEntity != null) {
            this.refreshSubjectDetailCache(subjectEntity);
        }
        // 返回查询到的用户信息
        return subjectEntity;
    }
    
    /**
     *  将专题信息转换成数据库实体
     *
     * @author: CYM-pai
     * @date: 2025/05/07 16:30
     **/
    private SubjectEntity convertEntity(SubjectBaseResponseVO subjectBaseResponseVO) {
        // 创建专题响应VO对象实例
        SubjectEntity subjectEntity = new SubjectEntity();
        
        // 使用Spring的Bean工具类进行属性复制，减少手动赋值的繁琐
        BeanUtils.copyProperties(subjectBaseResponseVO,subjectEntity);
        
        return subjectEntity;
    }
    
    /**
     * 缓存空值，防止缓存击穿
     *
     * @param subjectId
     */
    private void refreshSubjectNoneCache(Long subjectId) {
        String cacheKey = RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(String.valueOf(subjectId));
        // 文章详情我们使用string类型来缓存，每个文章有自己的失效时间，避免缓存雪崩
        redisUtil.set(cacheKey, WebConstant.CACHE_NONE, RedisKeyEnum.SUBJECT_DETAIL_CACHE.getExpireTime());
    }
    
    
    /**
     *  按作者分页查询专题信息（过滤已删除）
     *
     * @author: CYM-pai
     * @date: 2025/05/07 16:43
     **/
    public PAIPageResponseBeanUtil<List<SubjectBaseResponseVO>> getSubjectList(Integer pageNo, Integer pageSize, String author) {
        
        // 创建分页请求对象，指定页码、每页大小和排序方式
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "createDate"));
        
        // 只查询未删除的专题
        Page<SubjectEntity> subjectEntities = subjectDAO.findByAuthorAndDeleteFlag(
                author, 
                com.pai4j.common.enums.DeleteFlagCodeEnum.UN_DELETE.getCode(), 
                pageable);
        List<SubjectBaseResponseVO> subjects = new ArrayList<>();
        // 处理空数据：如果查询结果为空或内容为空，则直接返回空的文章列表。
        if (subjectEntities != null && !CollectionUtils.isEmpty(subjectEntities.getContent())) {
            //从分页的专题实体中批量获取用户id存入set集合中
            Set<String> userIds = subjectEntities.stream().map(SubjectEntity::getAuthor).collect(Collectors.toSet());
            // 走缓存批量查询用户信息
            Map<String, UserBaseResponseInfoVO> accounts = accountService.batchGet(userIds);
            // 批量构造返回文章列表参数
           
            subjects = subjectEntities.getContent().stream().map(a -> {
                SubjectBaseResponseVO subject = subjectAccountService.convertBaseVo(a);
                subject.setUserInfo(accounts.get(a.getAuthor()));
                return subject;
            }).collect(Collectors.toList());
        }
        // 构建并返回分页响应工具类
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, subjectEntities.getTotalElements(), subjects);
    }
    
    /**
     *  获取专题基础信息
     *
     * @author: CYM-pai
     * @date: 2025/05/07 16:49
     **/
    public SubjectBaseResponseVO getSubjectBaseInfo(Long subjectAccount) {
        
        //1、 先从缓存中获取专题信息
        SubjectBaseResponseVO subjectBaseResponseVO = this.findSubjectInfoFromCache(String.valueOf(subjectAccount));
        if (subjectBaseResponseVO == null) {
            // 缓存不存在主动查询数据库
            SubjectEntity subjectEntity = subjectDAO.findById(subjectAccount).orElse(null);
            if (subjectEntity == null) {
                // 数据库专题不存在，这里缓存空值，防止redis击穿
                this.refreshSubjectNoneCache(subjectAccount);
            }
            
           subjectBaseResponseVO = subjectAccountService.convertBaseVo(subjectEntity);
            //刷新缓存
            this.refreshSubjectRedisDetailCache(subjectEntity);
        }
        String author = subjectBaseResponseVO.getAuthor();
        UserBaseResponseInfoVO userBaseResponseInfoVO = accountService.accountInfo(author);
        subjectBaseResponseVO.setUserInfo(userBaseResponseInfoVO);
        return subjectBaseResponseVO;
    }
    
    
    /**
     * 根据传入的专题ID获取这个作者创建的其他专题Id和名称
     *
     * @author: CYM-pai
     * @date: 2025/05/09 21:04
     **/
    public Map<Long, String> getSubjectIdAndName(Long subjectId) {
        // 根据专题ID获取作者ID的方法
        String author = this.getAuthorIdBySubjectId(subjectId);
        // 查询该作者创建的所有专题
        List<SubjectBaseResponseVO> subjectEntities = subjectAccountService.getSubjectListFromCache(author);
        
        // 构建返回结果，包含当前subjectId
        Map<Long, String> result = new HashMap<>();
        for (SubjectBaseResponseVO subject : subjectEntities) {
            result.put(subject.getId(), subject.getName());
        }
        return result;
    }
    /**
     * 根据用户id获取这个作者创建的其他专题Id和名称
     *
     * @author: CYM-pai
     * @date: 2025/05/09 21:04
     **/
    public Map<Long, String> getSubjectIdAndName(String userId) {
        // 查询该作者创建的所有专题
        List<SubjectBaseResponseVO> subjectEntities = subjectAccountService.getSubjectListFromCache(userId);
        
        // 构建返回结果，包含当前subjectId
        Map<Long, String> result = new HashMap<>();
        for (SubjectBaseResponseVO subject : subjectEntities) {
            result.put(subject.getId(), subject.getName());
        }
        return result;
    }
    
    /**
     * 根据主题ID获取作者ID
     * <p>
     * 此方法通过调用subjectDAO的findById方法来查找指定主题ID的主题对象，
     * 如果找到，则返回该主题对象的作者ID；如果未找到，则返回null
     *
     * @param subjectId 主题ID，用于查找主题对象
     * @return 主题的作者ID，如果未找到则返回null
     */
    private String getAuthorIdBySubjectId(Long subjectId) {
        // 使用主题ID查找主题对象，如果找到则获取其作者ID
        String author = subjectDAO.findById(subjectId).orElse(null).getAuthor();
        // 返回作者ID
        return author;
    }
    
    
    
    /**
     * 获取专题推荐列表
     *
     * @param subjectId
     * @return
     */
    public SubjectBaseResponseVO getSubjectRecommand(Long subjectId) {
       SubjectBaseResponseVO subjectBaseResponseVO = this.getSubjectBaseInfo(subjectId);
        return subjectBaseResponseVO;
    }
    
    /**
     * 删除专题（逻辑删除，级联删除）
     * 将专题的 deleteFlag 标记为已删除，而不是物理删除数据
     * 同时会逻辑删除该专题下的所有文章（级联删除）
     * 这样可以保留专题和文章数据用于数据分析、审计和可能的恢复
     * 删除时会同时清除Redis缓存
     *
     * @param subjectId 专题ID
     * @param author 作者（用于权限验证）
     * @return 是否删除成功
     */
    public Boolean deleteSubject(Long subjectId, String author) {
        // 查找专题实体
        SubjectEntity subjectEntity = subjectDAO.findById(subjectId).orElse(null);
        Assert.notNull(subjectEntity, "专题不存在！");
        
        // 检查是否已被删除
        if (com.pai4j.common.enums.DeleteFlagCodeEnum.DELETE.getCode().equals(subjectEntity.getDeleteFlag())) {
            log.warn("专题已被删除: subjectId={}", subjectId);
            return true;
        }
        
        // 权限验证
        Assert.isTrue(ObjectUtils.equals(subjectEntity.getAuthor(), author), "没有删除权限！");
        
        // 1. 逻辑删除：设置删除标识和删除信息
        subjectEntity.setDeleteFlag(com.pai4j.common.enums.DeleteFlagCodeEnum.DELETE.getCode());
        subjectEntity.setDeleteBy(author);
        subjectEntity.setDeleteDate(new Date());
        subjectEntity.setUpdateBy(author);
        subjectEntity.setUpdateDate(new Date());
        
        // 保存删除标记
        subjectDAO.save(subjectEntity);
        
        // 2. 级联删除该专题下的所有未删除文章
        String subjectAccount = String.valueOf(subjectId);
        int deletedArticlesCount = cascadeDeleteSubjectArticles(subjectAccount, author);
        log.info("级联删除专题文章: subjectId={}, 删除文章数量={}", subjectId, deletedArticlesCount);
        
        // 3. 清除Redis缓存（已删除的专题不应该被缓存访问）
        String cacheKey = RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(String.valueOf(subjectId));
        redisUtil.remove(cacheKey);
        
        // 刷新作者的专题列表缓存
        subjectAccountService.refreshSubjectListCache(author);
        
        log.info("专题逻辑删除成功: subjectId={}, author={}, deleteFlag={}, 级联删除文章数={}", 
                subjectId, author, subjectEntity.getDeleteFlag(), deletedArticlesCount);
        return true;
    }
    
    /**
     * 级联删除专题下的所有文章（逻辑删除）
     * 该方法会查询专题下所有未删除的文章，并逐一进行逻辑删除
     * 同时清除文章的Redis缓存
     *
     * @param subjectAccount 专题账户
     * @param operator 操作人（删除人）
     * @return 成功删除的文章数量
     */
    private int cascadeDeleteSubjectArticles(String subjectAccount, String operator) {
        // 查询该专题下所有未删除的文章
        List<com.pai4j.pgc.entity.ArticleEntity> articles = articleDAO.findAllBySubjectAccountAndDeleteFlag(
                subjectAccount, 
                com.pai4j.common.enums.DeleteFlagCodeEnum.UN_DELETE.getCode());
        
        if (CollectionUtils.isEmpty(articles)) {
            log.debug("专题下没有需要删除的文章: subjectAccount={}", subjectAccount);
            return 0;
        }
        
        int deletedCount = 0;
        Date now = new Date();
        
        for (com.pai4j.pgc.entity.ArticleEntity article : articles) {
            // 逻辑删除：设置删除标识和删除信息
            article.setDeleteFlag(com.pai4j.common.enums.DeleteFlagCodeEnum.DELETE.getCode());
            article.setDeleteBy(operator);
            article.setDeleteDate(now);
            article.setUpdateBy(operator);
            article.setUpdateDate(now);
            
            // 保存删除标记
            articleDAO.save(article);
            
            // 清除Redis缓存
            String cacheKey = RedisKeyEnum.ARTICLE_DETAIL_CACHE.getKey(String.valueOf(article.getId()));
            redisUtil.remove(cacheKey);
            
            deletedCount++;
            log.debug("级联删除文章: articleId={}, subjectAccount={}", article.getId(), subjectAccount);
        }
        
        return deletedCount;
    }
    
    /**
     * 恢复已删除的专题（支持级联恢复）
     * 将逻辑删除的专题恢复为正常状态
     * 可选择是否同时恢复该专题下的已删除文章
     * 恢复时会重新加入缓存
     *
     * @param subjectId 专题ID
     * @param author 作者（用于权限验证）
     * @param cascadeRestore 是否级联恢复专题下的文章（true=恢复文章，false=只恢复专题）
     * @return 是否恢复成功
     */
    public Boolean restoreSubject(Long subjectId, String author, Boolean cascadeRestore) {
        // 查找专题实体
        SubjectEntity subjectEntity = subjectDAO.findById(subjectId).orElse(null);
        Assert.notNull(subjectEntity, "专题不存在！");
        
        // 检查是否已被删除
        Assert.isTrue(com.pai4j.common.enums.DeleteFlagCodeEnum.DELETE.getCode().equals(subjectEntity.getDeleteFlag()),
                "专题未被删除，无需恢复！");
        
        // 权限验证
        Assert.isTrue(ObjectUtils.equals(subjectEntity.getAuthor(), author), "没有恢复权限！");
        
        // 1. 恢复专题：清除删除标识
        subjectEntity.setDeleteFlag(com.pai4j.common.enums.DeleteFlagCodeEnum.UN_DELETE.getCode());
        subjectEntity.setDeleteBy(null);
        subjectEntity.setDeleteDate(null);
        subjectEntity.setUpdateBy(author);
        subjectEntity.setUpdateDate(new Date());
        
        // 保存恢复标记
        subjectDAO.save(subjectEntity);
        
        // 2. 如果需要级联恢复，则恢复该专题下的所有已删除文章
        int restoredArticlesCount = 0;
        if (Boolean.TRUE.equals(cascadeRestore)) {
            String subjectAccount = String.valueOf(subjectId);
            restoredArticlesCount = cascadeRestoreSubjectArticles(subjectAccount, author);
            log.info("级联恢复专题文章: subjectId={}, 恢复文章数量={}", subjectId, restoredArticlesCount);
        }
        
        // 3. 刷新Redis缓存
        this.refreshSubjectDetailCache(subjectEntity);
        
        // 刷新作者的专题列表缓存
        subjectAccountService.refreshSubjectListCache(author);
        
        log.info("专题恢复成功: subjectId={}, author={}, 级联恢复文章数={}", 
                subjectId, author, restoredArticlesCount);
        return true;
    }
    
    /**
     * 恢复已删除的专题（兼容旧接口，默认不级联恢复文章）
     * 将逻辑删除的专题恢复为正常状态
     * 恢复时会重新加入缓存
     *
     * @param subjectId 专题ID
     * @param author 作者（用于权限验证）
     * @return 是否恢复成功
     */
    public Boolean restoreSubject(Long subjectId, String author) {
        return restoreSubject(subjectId, author, false);
    }
    
    /**
     * 级联恢复专题下的所有已删除文章
     * 该方法会查询专题下所有已删除的文章，并逐一进行恢复
     * 同时刷新文章的Redis缓存
     *
     * @param subjectAccount 专题账户
     * @param operator 操作人（恢复人）
     * @return 成功恢复的文章数量
     */
    private int cascadeRestoreSubjectArticles(String subjectAccount, String operator) {
        // 查询该专题下所有已删除的文章
        List<com.pai4j.pgc.entity.ArticleEntity> articles = articleDAO.findAllBySubjectAccountAndDeleteFlag(
                subjectAccount, 
                com.pai4j.common.enums.DeleteFlagCodeEnum.DELETE.getCode());
        
        if (CollectionUtils.isEmpty(articles)) {
            log.debug("专题下没有需要恢复的文章: subjectAccount={}", subjectAccount);
            return 0;
        }
        
        int restoredCount = 0;
        Date now = new Date();
        
        for (com.pai4j.pgc.entity.ArticleEntity article : articles) {
            // 恢复：清除删除标识
            article.setDeleteFlag(com.pai4j.common.enums.DeleteFlagCodeEnum.UN_DELETE.getCode());
            article.setDeleteBy(null);
            article.setDeleteDate(null);
            article.setUpdateBy(operator);
            article.setUpdateDate(now);
            
            // 保存恢复标记
            articleDAO.save(article);
            
            // 刷新Redis缓存
            String cacheKey = RedisKeyEnum.ARTICLE_DETAIL_CACHE.getKey(String.valueOf(article.getId()));
            String articleJson = com.pai4j.common.util.JsonUtil.toJsonString(article);
            redisUtil.set(cacheKey, articleJson, RedisKeyEnum.ARTICLE_DETAIL_CACHE.getExpireTime());
            
            restoredCount++;
            log.debug("级联恢复文章: articleId={}, subjectAccount={}", article.getId(), subjectAccount);
        }
        
        return restoredCount;
    }
    
    /**
     * 分页查询指定作者已删除的专题
     * 用于回收站功能，方便用户查看和恢复已删除的专题
     *
     * @param pageNo 页码
     * @param pageSize 每页大小
     * @param author 作者
     * @return 已删除专题分页列表
     */
    public PAIPageResponseBeanUtil<List<SubjectBaseResponseVO>> pageDeletedByAuthor(Integer pageNo, Integer pageSize, String author) {
        // 构建分页请求：通过PageRequest.of方法创建分页参数，按删除时间降序排序
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, Sort.by(Sort.Direction.DESC, "deleteDate"));
        
        // 查询已删除的专题
        Page<SubjectEntity> subjectEntities = subjectDAO.findByAuthorAndDeleteFlag(
                author, 
                com.pai4j.common.enums.DeleteFlagCodeEnum.DELETE.getCode(), 
                pageable);
        List<SubjectBaseResponseVO> subjects = new ArrayList<>();
        
        // 处理空数据
        if (subjectEntities != null && !CollectionUtils.isEmpty(subjectEntities.getContent())) {
            // 从分页的专题实体中批量获取用户id
            Set<String> userIds = subjectEntities.stream()
                    .map(SubjectEntity::getAuthor)
                    .collect(Collectors.toSet());
            // 走缓存批量查询用户信息
            Map<String, UserBaseResponseInfoVO> accounts = accountService.batchGet(userIds);
            
            // 批量构造返回专题列表参数
            subjects = subjectEntities.getContent().stream().map(s -> {
                SubjectBaseResponseVO subject = subjectAccountService.convertBaseVo(s);
                subject.setUserInfo(accounts.get(s.getAuthor()));
                return subject;
            }).collect(Collectors.toSet()).stream().collect(Collectors.toList());
        }
        
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, subjectEntities.getTotalElements(), subjects);
    }
    
    /**
     * 统计指定作者的专题数量（按删除标识）
     *
     * @param author 作者
     * @param deleteFlag 删除标识（1=未删除，0=已删除）
     * @return 专题数量
     */
    public Long countSubjectsByAuthor(String author, Integer deleteFlag) {
        return subjectDAO.countByAuthorAndDeleteFlag(author, deleteFlag);
    }
    
    /**
     * 物理删除专题（管理员专用，危险操作！级联物理删除）
     * 直接从数据库中删除专题记录，无法恢复
     * 同时会物理删除该专题下的所有文章（级联物理删除）
     * 该操作需要管理员权限，请谨慎使用
     * 删除后会清除所有相关Redis缓存
     *
     * @param subjectId 专题ID
     * @param adminUser 管理员用户（用于审计记录）
     * @return 是否删除成功
     */
    public Boolean physicalDeleteSubject(Long subjectId, String adminUser) {
        // 查找专题实体
        SubjectEntity subjectEntity = subjectDAO.findById(subjectId).orElse(null);
        if (subjectEntity == null) {
            log.warn("物理删除专题失败：专题不存在 subjectId={}", subjectId);
            return false;
        }
        
        String author = subjectEntity.getAuthor();
        
        // 1. 级联物理删除该专题下的所有文章（包括已删除和未删除的）
        String subjectAccount = String.valueOf(subjectId);
        int deletedArticlesCount = cascadePhysicalDeleteSubjectArticles(subjectAccount, adminUser);
        log.warn("【管理员操作】级联物理删除专题文章: subjectId={}, 删除文章数量={}, admin={}", 
                subjectId, deletedArticlesCount, adminUser);
        
        // 2. 物理删除：直接从数据库中删除专题
        subjectDAO.delete(subjectEntity);
        
        // 3. 清除Redis缓存
        String cacheKey = RedisKeyEnum.SUBJECT_DETAIL_CACHE.getKey(String.valueOf(subjectId));
        redisUtil.remove(cacheKey);
        
        // 刷新作者的专题列表缓存
        subjectAccountService.refreshSubjectListCache(author);
        
        log.warn("【管理员操作】专题物理删除成功: subjectId={}, author={}, 级联删除文章数={}, admin={}", 
                subjectId, author, deletedArticlesCount, adminUser);
        return true;
    }
    
    /**
     * 级联物理删除专题下的所有文章
     * 该方法会查询专题下所有文章（包括已删除和未删除的），并逐一进行物理删除
     * 同时清除文章的Redis缓存
     *
     * @param subjectAccount 专题账户
     * @param adminUser 管理员用户（用于审计记录）
     * @return 成功删除的文章数量
     */
    private int cascadePhysicalDeleteSubjectArticles(String subjectAccount, String adminUser) {
        // 查询该专题下所有未删除的文章
        List<com.pai4j.pgc.entity.ArticleEntity> unDeletedArticles = articleDAO.findAllBySubjectAccountAndDeleteFlag(
                subjectAccount, 
                com.pai4j.common.enums.DeleteFlagCodeEnum.UN_DELETE.getCode());
        
        // 查询该专题下所有已删除的文章
        List<com.pai4j.pgc.entity.ArticleEntity> deletedArticles = articleDAO.findAllBySubjectAccountAndDeleteFlag(
                subjectAccount, 
                com.pai4j.common.enums.DeleteFlagCodeEnum.DELETE.getCode());
        
        // 合并所有文章列表
        List<com.pai4j.pgc.entity.ArticleEntity> allArticles = new ArrayList<>();
        if (!CollectionUtils.isEmpty(unDeletedArticles)) {
            allArticles.addAll(unDeletedArticles);
        }
        if (!CollectionUtils.isEmpty(deletedArticles)) {
            allArticles.addAll(deletedArticles);
        }
        
        if (CollectionUtils.isEmpty(allArticles)) {
            log.debug("专题下没有需要删除的文章: subjectAccount={}", subjectAccount);
            return 0;
        }
        
        int deletedCount = 0;
        
        for (com.pai4j.pgc.entity.ArticleEntity article : allArticles) {
            // 物理删除：直接从数据库中删除
            articleDAO.delete(article);
            
            // 清除Redis缓存
            String cacheKey = RedisKeyEnum.ARTICLE_DETAIL_CACHE.getKey(String.valueOf(article.getId()));
            redisUtil.remove(cacheKey);
            
            deletedCount++;
            log.debug("【管理员操作】级联物理删除文章: articleId={}, subjectAccount={}, admin={}", 
                    article.getId(), subjectAccount, adminUser);
        }
        
        return deletedCount;
    }
}